<?php

class Adept_Template_Node 
{

    protected $name;
    protected $children = array();
    protected $nodeId = null;
    protected $parent = null;

    protected $location;

    protected static $nodeIdGenerator = 1;

    public function __construct()
    {
        $this->name = "__node";
    }

    public function addChild($child)
    {
        if (isset($this->children[$child->getNodeId()])) {
            throw new Adept_Template_Exception("Duplicated node ids ", 
                array('nodeId' => $child->getNodeId()), $this->getLocation());
        }
        $this->children[$child->getNodeId()] = $child;
        $child->setParent($this);
    }

    public function findChild($nodeId, $recursive = true)
    {
        foreach ($this->children as $child) {
            if ($child->getNodeId() == $serverId) {
                return $child;
            } else {
                if (($result = $child->findChild($serverId)) != null) {
                    return $result;
                }
            }
        }
        return null;
    }

    public function findChildByClass($class, $recursive = true)
    {
        foreach ($this->children as $child) {
            if ($child instanceof $class) {
                return $child;
            } else {
                if ($recursive && !is_null($result = $child->findChildByClass($class))) {
                    return $result;
                }
            }
        }
        return null;
    }

    public function findChildrenByClass($class, $recursive = true)
    {
        $result = array();
        foreach ($this->children as $child) {
            if ($child instanceof $class) {
                $result[] = $child;
            }
            if ($recursive) {
                $result = array_merge($result, $child->findChildrenByClass($class));
            }
        }
        return $result;
    }

    public function findParentByClass($class)
    {
        $parent = $this->getParent();
        while (!is_null($parent)) {
            if ($parent instanceof $class) {
                return $parent;
            }
            $parent = $parent->getParent();
        }
        return null;
    }

    public function hasChildren()
    {
        return is_array($this->children) && (count($this->children) > 0);
    }
    
    public function getElementRefCode() 
    {
        return $this->parent->getElementRefCode();
    }	

    public function prepare()
    {
        foreach ($this->children as $child) {
            $child->prepare();
        }
    }

    public function generate($writer)
    {
        $this->generateBegin($writer);
        $this->generateChildren($writer);
        $this->generateEnd($writer);
    }

    public function generateBegin($writer)
    {

    }

    public function generateChildren($writer)
    {
        foreach ($this->children as $child) {
            $child->generate($writer);
        }
    }

    public function generateEnd($writer)
    {

    }

    public function preParse($compiler)
    {
        foreach ($this->children as $child) {
            $child->preParse($compiler);
        }
    }

    public function getChildren()
    {
        return $this->hasChildren() ? $this->children : array();
    }

    public function getLocation()
    {
        return $this->location;
    }

    public function setLocation($location)
    {
        $this->location = $location;
    }

    public function getParent()
    {
        return $this->parent;
    }

    public function setParent($parent)
    {
        $this->parent = $parent;
    }

    public function getRoot()
    {
        return $this->parent->getRoot();
    }

    public function getNodeId()
    {
        if (!isset($this->nodeId)) {
            $this->nodeId = 'node' . self::$nodeIdGenerator++;
        }
        return $this->nodeId;
    }

    public function setNodeId($nodeId)
    {
        $this->nodeId = $nodeId;
    }
    
    public function getName() 
    {
        return $this->name;
    }
    
    public function setName($name) 
    {
        $this->name = $name;
    }

}
